package com.example.services;

import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

import com.example.ShoppingCartApiApplication;
import com.example.dtos.ItemToPurchaseDTO;
import com.example.exceptions.ProductNotFoundException;
import com.example.models.ItemToPurchase;
import com.example.models.Product;
import com.example.repositories.ProductRepository;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ShoppingCartApiApplication.class)
@WebAppConfiguration
@IntegrationTest({"server.port=9090"})
public class ProductServiceIT {

	private Product product1, product2, product3;
	
	@Autowired
    private ProductRepository productRepository;
	
	@Autowired
    private ProductService productService;
	
	@Autowired
    private WebApplicationContext webApplicationContext;
	
	ArrayList<Product> products = new ArrayList<Product>();
	SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
    DateFormat df = new SimpleDateFormat("dd-MM-yyyy");
	
	@Before
    public void setup() throws Exception {

        this.productRepository.deleteAll();
        
        product1 = productRepository.save(new Product("p1Id","food","pizza",50.55,100, formatter.parse("10-01-2021")));
        product2 = productRepository.save(new Product("p2Id","drink","beer",26.90,300, formatter.parse("10-01-2022")));
        products.add(product1);
        products.add(product2);
    }
	
	@Test
	public void getProduct() throws Exception{
		Product result = productService.find(product1.getId());
		assertEquals(product1.getName(), result.getName());
		assertEquals(product1.getCategory(), result.getCategory());
		assertEquals(String.valueOf(product1.getPrice()), String.valueOf(result.getPrice()));
		assertEquals(product1.getStock(), result.getStock());
	}
    
	@Test(expected = ProductNotFoundException.class) 
	public void productNotFound() throws Exception{
		productService.find("123");
	}
	
	@Test
	public void editProduct() throws Exception {
		productService.editProduct(product1.getId(),product1);
	}
	
	 @Test (expected = ProductNotFoundException.class)
	 public void editProductWhenNotFound() throws Exception{
		 productService.editProduct("123", product1);
	 }
	 
	@Test
	public void deleteProduct() throws Exception {
			productService.deleteProduct(product1.getId());
	}

	@Test (expected = ProductNotFoundException.class) 
	public void deleteProductWhenNotFound() throws Exception {
			productService.deleteProduct("123");
	}
	
	@Test
	public void getAllProducts(){
		Collection<Product> result = productService.findProducts(null, null, null);
		Iterator<Product> iterator = result.iterator();
		int index=0;
		while(iterator.hasNext()){
			Product next = iterator.next();
			assertEquals(products.get(index).getName(), next.getName());
			assertEquals(products.get(index).getCategory(), next.getCategory());
			assertEquals(String.valueOf(products.get(index).getPrice()), String.valueOf(next.getPrice()));
			assertEquals(products.get(index).getStock(), next.getStock());
			index++;
		}
	}
	
	@Test
	public void getAllSortedBy() throws Exception {
        product3 = productRepository.save(new Product("3","food", "apple",5,200, formatter.parse("10-01-2023")));
		products.add(product3);
		Collections.sort(products,(prod1, prod2) -> 
										( ((Product)prod1).getStock()<((Product)prod2).getStock() ? -1 : 
											(((Product)prod1).getStock()==((Product)prod2).getStock() ? 0 : 
																									1)));
		Collection<Product> result = productService.findProducts(null, null, new Sort(Direction.ASC, "stock"));
		Iterator<Product> iterator = result.iterator();
		int index=0;
		while(iterator.hasNext()){
			Product next = iterator.next();
			assertEquals(products.get(index).getName(), next.getName());
			assertEquals(products.get(index).getCategory(), next.getCategory());
			assertEquals(String.valueOf(products.get(index).getPrice()), String.valueOf(next.getPrice()));
			assertEquals(products.get(index).getStock(), next.getStock());
			index++;
		}
	}
	
	@Test
	public void getAllPaginated() throws Exception {
		product3 = productRepository.save(new Product("3","food", "apple",5,200, formatter.parse("10-01-2023")));
		products.add(product3);
		Collection<Product> result = productService.findProducts(0, 2, null);
		assertTrue(result.size()==2);
		Iterator<Product> iterator = result.iterator();
		int index=0;
		while(iterator.hasNext()){
			Product next = iterator.next();
			assertEquals(products.get(index).getName(), next.getName());
			assertEquals(products.get(index).getCategory(), next.getCategory());
			assertEquals(String.valueOf(products.get(index).getPrice()), String.valueOf(next.getPrice()));
			assertEquals(products.get(index).getStock(), next.getStock());
			index++;
		}
		
		result = productService.findProducts(1, 2,  null);
		assertTrue(result.size()==1);
		Product found = result.iterator().next();
		assertEquals(product3.getName(), found.getName());
		assertEquals(product3.getCategory(), found.getCategory());
		assertEquals(String.valueOf(product3.getPrice()), String.valueOf(found.getPrice()));
		assertEquals(product3.getStock(), found.getStock());
	}
	
	@Test
	public void readAllPaginatedAndSorted() throws Exception {
		//p1: food p2: drink p3: fruit
		product3 = productRepository.save(new Product("3","fruit", "apple",5,200, formatter.parse("10-01-2023")));
		products.add(product3);
		Collection<Product> result = productService.findProducts(0, 2, new Sort(Direction.DESC, "category"));
		assertTrue(result.size()==2);
		
		
		result = productService.findProducts(1, 2, new Sort(Direction.DESC, "category"));
		assertTrue(result.size()==1);
		Product found = result.iterator().next();
		assertEquals(product2.getName(), found.getName());
		assertEquals(product2.getCategory(), found.getCategory());
		assertEquals(String.valueOf(product2.getPrice()), String.valueOf(found.getPrice()));
		assertEquals(product2.getStock(), found.getStock());
		
	}
	
//	 @Test
//	 public void buyProduct() throws Exception {
//		 productService.buy(new ItemToPurchase("item_id", product1.getId(),product1.getStock(),null));
//	 }
//	 
//	 @Test (expected = ProductNotFoundException.class)
//	 public void buyNonExistentProduct() throws Exception {
//		 productService.buy(new ItemToPurchase("item_id", "123",10,null));
//	 }
//	 
//	 @Test (expected = NotEnoughProductStock.class)
//	 public void buyZeroItemsOfProduct() throws Exception {
//		 productService.buy(new ItemToPurchase("item_id", product1.getId(),product1.getStock()+1,null));
//	 }
	 
	 @Test
	 public void readProductsWithCategory() throws Exception {		 
		 Collection<Product> zeroProducts = productService.productsWithCategory("cuchuflo");
		 assertTrue(zeroProducts.isEmpty());

		 Collection<Product> collectionWithFirstProduct = productService.productsWithCategory(product1.getCategory());
		 assertTrue(collectionWithFirstProduct.size()==1);
		 Iterator<Product> iterator = collectionWithFirstProduct.iterator();
		 while(iterator.hasNext()){
				Product next = iterator.next();
				assertEquals(product1.getName(), next.getName());
				assertEquals(product1.getCategory(), next.getCategory());
				assertEquals(String.valueOf(product1.getPrice()), String.valueOf(next.getPrice()));
				assertEquals(product1.getStock(), next.getStock());
		}
		 
		 Collection<Product> collectionWithSecondProduct = productService.productsWithCategory(product2.getCategory());
		 assertTrue(collectionWithSecondProduct.size()==1);
		 iterator = collectionWithSecondProduct.iterator();
		 while(iterator.hasNext()){
				Product next = iterator.next();
				assertEquals(product2.getName(), next.getName());
				assertEquals(product2.getCategory(), next.getCategory());
				assertEquals(String.valueOf(product2.getPrice()), String.valueOf(next.getPrice()));
				assertEquals(product2.getStock(), next.getStock());
		 }
	    }
	 
	 @Test
	 public void readProductsWithName() throws Exception {		 	
		 Collection<Product> zeroProducts = productService.productsWithName("cuchuflo");
		 assertTrue(zeroProducts.isEmpty());

		 Collection<Product> collectionWithFirstProduct = productService.productsWithName(product1.getName());
		 assertTrue(collectionWithFirstProduct.size()==1);
		 Iterator<Product> iterator = collectionWithFirstProduct.iterator();
		 while(iterator.hasNext()){
				Product next = iterator.next();
				assertEquals(product1.getName(), next.getName());
				assertEquals(product1.getCategory(), next.getCategory());
				assertEquals(String.valueOf(product1.getPrice()), String.valueOf(next.getPrice()));
				assertEquals(product1.getStock(), next.getStock());
		}
		 
		 Collection<Product> collectionWithSecondProduct = productService.productsWithName(product2.getName());
		 assertTrue(collectionWithSecondProduct.size()==1);
		 iterator = collectionWithSecondProduct.iterator();
		 while(iterator.hasNext()){
				Product next = iterator.next();
				assertEquals(product2.getName(), next.getName());
				assertEquals(product2.getCategory(), next.getCategory());
				assertEquals(String.valueOf(product2.getPrice()), String.valueOf(next.getPrice()));
				assertEquals(product2.getStock(), next.getStock());
		 }  
	    }
	 
	 @Test
	 public void readProductsWithDateBetween() throws Exception {
		 product3 = productRepository.save(new Product("3","fruit", "apple",5,200, formatter.parse("10-01-2023")));
		 products.add(product3);
		 
		 String date1="10-01-2018";
		 String date2="10-12-2022";
		 
		 Collection<Product> collection = productService.productsWithExpirationDateBetween(formatter.parse(date1), formatter.parse(date2));
		 assertTrue(collection.size()==2);
		 Iterator<Product> iterator = collection.iterator();
		 iterator = collection.iterator();
		 int index=0;
		 while(iterator.hasNext()){
				Product next = iterator.next();
				assertEquals(products.get(index).getName(), next.getName());
				assertEquals(products.get(index).getCategory(), next.getCategory());
				assertEquals(String.valueOf(products.get(index).getPrice()), String.valueOf(next.getPrice()));
				assertEquals(products.get(index).getStock(), next.getStock());
				index++;
		}     
	   }
}
